﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using MarvinsArena.Core;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MarvinsArena.Robot.Core;

namespace BattleLogic
{
	public class Logic:IGame 
	{
		private ScoreKeeper scoreKeeper;
		private Round round;
		private Tournament tournament;
		private Controls controls;

		public IHudMessages hudMessages { get; set; }
		public IMap map {get; set;}
		public ICamera camera { get; set; }

		public List<IProjectile> projectiles {get;set;}
		public List<IRobot> robots
		{
			get
			{
                List<IRobot> r = new List<IRobot>();
                foreach (Robot robot in robotsLogic)
				{
					r.Add((IRobot)robot);
				}
				return r;
			}
			set
			{
				robots = value;
			}
		}

		private HudMessages hudMessagesLogic { get { return (HudMessages)this.hudMessages; } }
		private Map mapLogic { get { return (Map)this.map; } }
		private Camera cameraLogic { get { return (Camera)this.camera; } }
		private List<Robot> robotsLogic;
		


		public bool isExit { get; set; }
		public bool isActive { get; set; }

		public Logic(Tournament tournament)
		{
			this.tournament = tournament;

            this.projectiles = new List<IProjectile>();
            this.robotsLogic = new List<Robot>();
		}

		public void InitializeLogic()
		{
			this.scoreKeeper = new ScoreKeeper();
			this.controls = new Controls();

			this.round = new Round();
			this.round.BeginGame += new RoundEventHandler(BeginGame);
			this.round.RoundBegining += new RoundEventHandler(RoundBegining);
			this.round.EndGame += new RoundEventHandler(EndGame);
			
			this.hudMessages = new HudMessages();
			this.map = new Map(this.tournament.Map);
			this.camera = new Camera();
		}

		private void BeginGame()
		{
			//Show satus message about starting new game
			this.hudMessagesLogic.StatusMessage = String.Format(Resourses.Hud.NewGame, 1 , this.tournament.Rules.Rounds).Replace("\\n", Environment.NewLine);
            this.hudMessagesLogic.isStatusMessageActive = true;
		}

		public void Update(GameTime gameTime)
		{
			//Chekc controls
			this.controls.Check(Keyboard.GetState(), Mouse.GetState());
			
			//Exit from game
			this.isExit = controls.isExit;

			if (this.round.isNewRound)
				round.BeginRound();

            if (controls.isBeginNewRound && this.hudMessages.isStatusMessageActive)
			{
				this.hudMessagesLogic.isStatusMessageActive = false;

				var teamsExit = from robot in robotsLogic
								group robot by robot.Team into robotGroup
								select robotGroup.Key;
				int teamsCountExit = teamsExit.Count();
				if (this.round.NumberRound == this.tournament.Rules.Rounds && (robotsLogic.Count < 2 || teamsCountExit < 2))
					this.isExit = true;

                this.isActive = true;
			}

			// --------------- Game is active----------
            if (this.isActive && !this.hudMessagesLogic.isStatusMessageActive)
			{
				//Change camera view
				if (controls.isChangeView)
					this.cameraLogic.NextTarget(this.robotsLogic.Count);

				//Move camera
				this.cameraLogic.Move(controls);

				//Update robots
				foreach (IRobot robot in this.robotsLogic)
				{
					((ILogicObject)robot).Update(gameTime.TotalGameTime.TotalMilliseconds);
				}

				//Update projectiles
				foreach (IProjectile projectile in this.projectiles)
				{
					((ILogicObject)projectiles).Update(gameTime.TotalGameTime.TotalMilliseconds);
				}

				//Check collision and add hud messages
				LogicCollision collision = new LogicCollision();
				hudMessagesLogic.AddMessage(collision.Check(this.robotsLogic, this.mapLogic, this.projectiles));

				//Remove dead projectiles
				projectiles.RemoveAll(obj => ((ILogicObject)obj).Dead == true);

				foreach (IRobot robot in robotsLogic)
				{
					((Robot)robot).Notify();
				}

				//Find and remove dead robots
				var deadRobotList = from robot in robotsLogic
									where robot.Hitpoints < 1
									select robot;

				foreach (IRobot t in deadRobotList)
				{
					hudMessagesLogic.AddMessage(String.Format(Resourses.Hud.RobotDied, t.FullName), true);
				}

				robotsLogic.RemoveAll(obj => obj.Hitpoints < 1);

				//Find and remove disabled robots
				var disabledrobotlist = from robot in robotsLogic
										where robot.IsActive == false
										select robot;
				foreach (IRobot t in disabledrobotlist)
				{
					hudMessagesLogic.AddMessage(String.Format(Resourses.Hud.RobotDisabled, t.FullName, t.DisabledMessage), true);
				}
				robotsLogic.RemoveAll(obj => obj.IsActive == false);

				//Determine winner, save tournament and inform hud
				var teams = from robot in robotsLogic
							group robot by robot.Team into robotGroup
							select robotGroup.Key;
				int teamsCount = teams.Count();

				if (robotsLogic.Count == 0 || teamsCount == 0)
				{
					hudMessagesLogic.StatusMessage = "Draw!";
				}
				else if (robotsLogic.Count == 1 || teamsCount == 1)
				{
					hudMessagesLogic.StatusMessage = string.Format(Resourses.Hud.RobotWins, robotsLogic[0].FullName);
					scoreKeeper.AddScore(robotsLogic[0].Team.ToString());
				}

				// Check for next round or game is over
				if (robotsLogic.Count < 2 || teamsCount < 2)
					if (round.NumberRound == this.tournament.Rules.Rounds)
						this.round.EndRound();
			}

			//Update hud
			this.hudMessagesLogic.Update();
		}

		/// <summary>
		/// Begin new round
		/// </summary>
		private void RoundBegining()
		{
			this.projectiles.Clear();
			this.robotsLogic.Clear();
			
			//Show message about new round
			hudMessagesLogic.AddMessage(string.Format(Resourses.Hud.NewRound, this.round.NumberRound), true);

			//Show score
			foreach (KeyValuePair<string, int> score in scoreKeeper.GetAllScores())
			{
				hudMessagesLogic.AddMessage(
					String.Format(score.Value != 1 ? Resourses.Hud.TeamPointSingle : Resourses.Hud.TeamPointSingle,	score.Key, score.Value));
			}

			//Add robots in game
            this.AddRobots();

			//Set camera select nothing
			this.camera.TargetIndex = this.camera.EmptyTarget;

			//Set status message about current round
			if (!this.round.isNewGame)
				this.hudMessagesLogic.StatusMessage = String.Format(Resourses.Hud.CurrentRound, this.hudMessagesLogic.StatusMessage,
					this.round.NumberRound, this.tournament.Rules.Rounds);

            //Sep camera on default screen
            cameraLogic.MoveOnDefaultPosition = true;
		}

		/// <summary>
		/// Create new robots in pool
		/// </summary>
		private void AddRobots()
		{
			Collection<IRobot> createdRobots = this.tournament.RobotManager.CreateRobots(this.tournament);
			foreach (IRobot robot in createdRobots)
			{
				// Assign all event handler
                Robot r = new Robot(robot);
				r.HandleRobotEvents += RobotCore_HandleRobotEvents;

                // Set position to free map area
                this.mapLogic.PlaceRobot(robot);

                // Set round values after positioning the object so positions are correct for notification event
                          r.SetRoundValues(this.tournament.Rules);

                // Add robot to the list
                this.robotsLogic.Add(r);
			}
		}

		/// <summary>
		/// Event from robot
		/// </summary>
		/// <param name="sender">The robot raising the event</param>
		/// <param name="e">The event arguments</param>
		void RobotCore_HandleRobotEvents(object sender, MarvinsArena.Robot.Core.MessageExchangeFromRobotEventArgs e)
		{
			Robot senderRobot = (Robot)sender;

			if (e.FireProjectile != null)
			{
				IProjectile projectile;

				if (e.FireProjectile.FireType == ProjectileType.Bullet)
					projectile = new Bullet(e.FireProjectile.Power);
				else
					projectile = new Missile();

				projectile.PositionX = (float)senderRobot.PositionX;
				projectile.PositionY = (float)senderRobot.PositionY;
				projectile.Rotation = (float)senderRobot.RotationGun;
				// Move away from spawn position immediately or this would collide with the spawning robot
				projectile.PositionX += 30.0f * (float)Math.Cos(projectile.Rotation);
				projectile.PositionY += 30.0f * (float)Math.Sin(projectile.Rotation);
				projectiles.Add(projectile);
			}

			foreach (string msg in e.PrintMessage)
			{
				this.hudMessagesLogic.AddMessage(String.Format(Resourses.Hud.RobotSays, senderRobot.FullName, msg));
			}

			foreach (CoreMessageReceivedFromTeammateEventArgs tmsg in e.TeamMessage)
			{
				foreach (IRobot robot in this.robotsLogic)
				{
					if (robot.Team == tmsg.Team && robot.SquadNumber != tmsg.SquadNumber)
						((Robot)robot).NotifyMessageToTeammate(tmsg);
				}
			}

			if (CoreMain.Instance.Debug)
				foreach (string msg in e.DebugMessage)
				{
					this.hudMessagesLogic.AddMessage(String.Format(Resourses.Hud.Debug, senderRobot.FullName, msg));
				}
		}

		private void EndGame()
		{
			List<string> names = scoreKeeper.GetHighscoreName().ToList();
			if (names.Count() > 1)
			{
				hudMessagesLogic.StatusMessage = Resourses.Hud.EndGameDraw;
			}
			else
			{
				int winnerTeam = Convert.ToInt32(names[0]);

				hudMessagesLogic.StatusMessage = string.Format(Resourses.Hud.TeamWon + winnerTeam);

				foreach (KeyValuePair<string, int> score in scoreKeeper.GetAllScores())
				{
					hudMessagesLogic.AddMessage(
					String.Format(score.Value != 1 ? Resourses.Hud.TeamPointSingle : Resourses.Hud.TeamPointSingle, score.Key, score.Value)); 
				}

				this.tournament.Bracket.SetWinner(winnerTeam);

				//Save result
				this.tournament.SaveToXml();
			}
		}
	}
}
